﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections;

namespace QlrrPrintService.Print
{
    public class TSCUSBPrinter : TSCPrinter, IDisposable
    {
        #region 静态方法

        //以下是调用windows的API的函数
        //获得GUID
        [DllImport("hid.dll")]
        public static extern void HidD_GetHidGuid(ref Guid HidGuid);
        static Guid guidHID = Guid.Empty;

        [DllImport("setupapi.dll", SetLastError = true)]
        public static extern unsafe IntPtr SetupDiGetClassDevs(
            ref Guid lpGuid,
            IntPtr Enumerator,
            IntPtr hwndParent,
            ClassDevsFlags Flags
            );



        //获取设备，true获取到
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);



        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct SP_DEVICE_INTERFACE_DATA
        {
            public int cbSize;
            public Guid interfaceClassGuid;
            public int flags;
            public IntPtr reserved;
        }



        // 获取接口的详细信息 必须调用两次 第1次返回长度 第2次获取数据 
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr deviceInfoSet,
            ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
            IntPtr deviceInterfaceDetailData,
            uint deviceInterfaceDetailDataSize,
            ref uint requiredSize,
            SP_DEVINFO_DATA deviceInfoData);


        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr deviceInfoSet,
            ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
            ref PSP_DEVICE_INTERFACE_DETAIL_DATA myPSP_DEVICE_INTERFACE_DETAIL_DATA,
            uint deviceInterfaceDetailDataSize,
            ref uint requiredSize,
            SP_DEVINFO_DATA deviceInfoData);


        public unsafe struct PSP_DEVICE_INTERFACE_DETAIL_DATA
        {
            public int cbSize;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string DevicePath;
        }



        [StructLayout(LayoutKind.Sequential)]
        public struct SP_DEVINFO_DATA
        {
            public int cbSize;
            public Guid ClassGuid;
            public int DevInst;
            public IntPtr Reserved;
        }


        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct SP_DEVICE_INTERFACE_DETAIL_DATA // user made struct to store device path
        {
            public uint cbSize;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string DevicePath;
        }

        public enum DIGCF
        {
            DIGCF_DEFAULT = 0x1,
            DIGCF_PRESENT = 0x2,
            DIGCF_ALLCLASSES = 0x4,
            DIGCF_PROFILE = 0x8,
            DIGCF_DEVICEINTERFACE = 0x10
        }

        //获取设备文件
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int CreateFile(
            string lpFileName,                            // file name
            uint dwDesiredAccess,                        // access mode
            uint dwShareMode,                            // share mode
            IntPtr lpSecurityAttributes,                    // SD
            uint dwCreationDisposition,                    // how to create
            uint dwFlagsAndAttributes,                    // file attributes
            IntPtr hTemplateFile                            // handle to template file
            );



        //external
        [DllImport("setupapi.dll", SetLastError = true)]
        public static extern unsafe int SetupDiEnumDeviceInfo(
            IntPtr hFile,
            int Index,
            ref  SP_DEVINFO_DATA DeviceInfoData
            );


        [DllImport("setupapi.dll", SetLastError = true)]
        public static extern unsafe int SetupDiGetDeviceRegistryProperty(
            IntPtr DeviceInfoSet,
            ref SP_DEVINFO_DATA DeviceInfoData,
            RegPropertyType Property,
            IntPtr PropertyRegDataType,
            ref DATA_BUFFER PropertyBuffer,
            int PropertyBufferSize,
            IntPtr RequiredSize
        );



        // Device interface detail data
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public unsafe struct DATA_BUFFER
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
            public string Buffer;
        }


        //读取设备文件
        [DllImport("Kernel32.dll", SetLastError = true)]
        private static extern bool ReadFile
        (
                IntPtr hFile,
                byte[] lpBuffer,
                uint nNumberOfBytesToRead,
                ref uint lpNumberOfBytesRead,
                IntPtr lpOverlapped
        );

        [DllImport("Kernel32.dll", SetLastError = true)]
        private static extern bool ReadFile
        (
                IntPtr hFile,
                byte[] lpBuffer,
                uint nNumberOfBytesToRead,
                ref uint lpNumberOfBytesRead,
                ref NativeOverlapped lpOverlapped
        );


        //寫入设备文件
        [DllImport("Kernel32.dll", SetLastError = true)]
        private static extern bool WriteFile
        (
            IntPtr hFile,
            byte[] lpBuffer,
            uint nNumberOfBytesToWrite,
            ref uint lpNumberOfBytesRead,
            ref NativeOverlapped lpOverlapped
        );

        /*
        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern bool WriteFile(
            IntPtr hFile,
            byte[] lpBuffer,
            uint nNumberOfBytesToWrite,
            out uint lpNumberOfBytesWritten,
            ref NativeOverlapped lpOverlapped);
        */

        //关闭访问设备句柄，结束进程的时候把这个加上保险点
        [DllImport("kernel32.dll")]
        static public extern int CloseHandle(IntPtr hObject);


        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetOverlappedResult(IntPtr hFile,
           [In] ref System.Threading.NativeOverlapped lpOverlapped,
           out uint lpNumberOfBytesTransferred, bool bWait);


        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CancelIo(IntPtr hFile);


        [Flags]
        public enum ClassDevsFlags
        {
            DIGCF_DEFAULT = 0x00000001,
            DIGCF_PRESENT = 0x00000002,
            DIGCF_ALLCLASSES = 0x00000004,
            DIGCF_PROFILE = 0x00000008,
            DIGCF_DEVICEINTERFACE = 0x00000010,
        }

        public enum RegPropertyType
        {
            SPDRP_DEVICEDESC = 0x00000000, // DeviceDesc (R/W)
            SPDRP_HARDWAREID = 0x00000001, // HardwareID (R/W)
            SPDRP_COMPATIBLEIDS = 0x00000002, // CompatibleIDs (R/W)
            SPDRP_UNUSED0 = 0x00000003, // unused
            SPDRP_SERVICE = 0x00000004, // Service (R/W)
            SPDRP_UNUSED1 = 0x00000005, // unused
            SPDRP_UNUSED2 = 0x00000006, // unused
            SPDRP_CLASS = 0x00000007, // Class (R--tied to ClassGUID)
            SPDRP_CLASSGUID = 0x00000008, // ClassGUID (R/W)
            SPDRP_DRIVER = 0x00000009, // Driver (R/W)
            SPDRP_CONFIGFLAGS = 0x0000000A, // ConfigFlags (R/W)
            SPDRP_MFG = 0x0000000B, // Mfg (R/W)
            SPDRP_FRIENDLYNAME = 0x0000000C, // FriendlyName (R/W)
            SPDRP_LOCATION_INFORMATION = 0x0000000D,// LocationInformation (R/W)
            SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E, // PhysicalDeviceObjectName (R)
            SPDRP_CAPABILITIES = 0x0000000F, // Capabilities (R)
            SPDRP_UI_NUMBER = 0x00000010, // UiNumber (R)
            SPDRP_UPPERFILTERS = 0x00000011, // UpperFilters (R/W)
            SPDRP_LOWERFILTERS = 0x00000012, // LowerFilters (R/W)
            SPDRP_BUSTYPEGUID = 0x00000013, // BusTypeGUID (R)
            SPDRP_LEGACYBUSTYPE = 0x00000014, // LegacyBusType (R)
            SPDRP_BUSNUMBER = 0x00000015, // BusNumber (R)
            SPDRP_ENUMERATOR_NAME = 0x00000016, // Enumerator Name (R)
            SPDRP_SECURITY = 0x00000017, // Security (R/W, binary form)
            SPDRP_SECURITY_SDS = 0x00000018, // Security (W, SDS form)
            SPDRP_DEVTYPE = 0x00000019, // Device Type (R/W)
            SPDRP_EXCLUSIVE = 0x0000001A, // Device is exclusive-access (R/W)
            SPDRP_CHARACTERISTICS = 0x0000001B, // Device Characteristics (R/W)
            SPDRP_ADDRESS = 0x0000001C, // Device Address (R)
            SPDRP_UI_NUMBER_DESC_FORMAT = 0x0000001E, // UiNumberDescFormat (R/W)
            SPDRP_MAXIMUM_PROPERTY = 0x0000001F  // Upper bound on ordinals
        }


        #endregion

        #region 变量

        static int HidHandle = -1;
        public const uint GENERIC_READ = 0x80000000;
        public const uint GENERIC_WRITE = 0x40000000;
        public const uint FILE_SHARE_READ = 0x00000001;
        public const uint FILE_SHARE_WRITE = 0x00000002;
        public const int OPEN_EXISTING = 3;



        const UInt32 INFINITE = 0xFFFFFFFF;
        const UInt32 WAIT_ABANDONED = 0x00000080;
        const UInt32 WAIT_IO_COMPLETION = 0x000000C0;
        const UInt32 WAIT_OBJECT_0 = 0x00000000;
        const UInt32 WAIT_TIMEOUT = 0x00000102;
        const UInt32 WAIT_FAILED = 0xFFFFFFFF;

        Guid GUID_DEVINTERFACE_USB_HUB = new Guid("f18a0e88-c30c-11d0-8815-00a0c906bed8");
        Guid GUID_DEVINTERFACE_USB_DEVICE = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED");
        Guid GUID_DEVINTERFACE_USB_HOST_CONTROLLER = new Guid("3ABF6F2D-71C4-462a-8A92-1E6861E6AF27");
        Guid GUID_USB_WMI_STD_DATA = new Guid("4E623B20-CB14-11D1-B331-00A0C959BBD2");
        Guid GUID_USB_WMI_STD_NOTIFICATION = new Guid("4E623B20-CB14-11D1-B331-00A0C959BBD2");
        Guid GUID_DEVINTERFACE_HID = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");

        Guid guid = new Guid("{A5DCBF10-6530-11D2-901F-00C04FB951ED}");
        Guid deviceguid = new Guid("{36FC9E60-C465-11CF-8056-444553540000}");

        private Guid GUID_DEVINTERFACE_USBPRINT = new Guid(0x28d78fad, 0x5a12, 0x11D1, 0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2);
        private Guid search_device = new Guid(0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed);



        public const int DIGCF_PRESENT = 0x02;
        public const int DIGCF_DEVICEINTERFACE = 0x10;

        private string VID = "";
        private string PID = "";

        private uint UsbWaitTime = 2000;

        ArrayList HIDUSBAddress = new ArrayList();

        IntPtr PnPHandle;

        #endregion

        #region private methods

        private unsafe string GetRegistryProperty(IntPtr PnPHandle, ref SP_DEVINFO_DATA DeviceInfoData, RegPropertyType Property)
        {
            DATA_BUFFER Buffer = new DATA_BUFFER();

            int result = SetupDiGetDeviceRegistryProperty(
                PnPHandle,
                ref DeviceInfoData,
                Property,
                IntPtr.Zero,
                ref Buffer,
                1024,
                IntPtr.Zero
                );


            return Buffer.Buffer;

        }

        private int WriteToStream(byte[] feedbyte)
        {
            //Overlapped overlapped = new Overlapped();
            uint nByte = 0;
            NativeOverlapped oswrite = new NativeOverlapped();
            oswrite.EventHandle = CreateEvent(IntPtr.Zero, true, false, null);
            uint dwRes;
            //IntPtr osWrite = CreateEvent(IntPtr.Zero, true, false, null);

            if (oswrite.EventHandle == null)
                return 0;

            //string feed = "FEED 100\n";
            uint bytewrite = 0;
            //byte[] feedbyte = System.Text.Encoding.Default.GetBytes(command);
            if (!WriteFile((IntPtr)HidHandle, feedbyte, (uint)feedbyte.Length, ref bytewrite, ref oswrite))
            {
                return 0;

            }

            while (true)
            {
                dwRes = WaitForSingleObject(oswrite.EventHandle, INFINITE);
                switch (dwRes)
                {
                    case WAIT_OBJECT_0:
                        if (!GetOverlappedResult((IntPtr)HidHandle, ref oswrite, out nByte, false))
                        {

                        }
                        Console.WriteLine(nByte);
                        //MessageBox.Show("WAIT_OBJECT_0");
                        break;
                    case WAIT_OBJECT_0 + 1:
                        //MessageBox.Show("WAIT_OBJECT_0 + 1");
                        break;
                    case WAIT_TIMEOUT:
                        //MessageBox.Show("WAIT_TIMEOUT");
                        break;
                    case WAIT_FAILED:
                        //MessageBox.Show("WAIT_FAILED");
                        break;
                    default:
                        //MessageBox.Show("default");
                        CancelIo((IntPtr)HidHandle);
                        nByte = 0;
                        break;

                }

                CloseHandle(oswrite.EventHandle);
                return 1;
            }


        }


        private int ReadToStream()
        {
            var readbuffer = new byte[1024];
            NativeOverlapped osReader = new NativeOverlapped();
            osReader.EventHandle = CreateEvent(IntPtr.Zero, false, false, null);
            bool ok;
            uint bytewrite = 0;
            uint dwRes;
            uint nByte = 0;
            if (osReader.EventHandle == null)
                return 0;

            Thread.Sleep(1000);
            bool recieve = ReadFile((IntPtr)HidHandle, readbuffer, (uint)readbuffer.Length, ref bytewrite, ref osReader);
            string qq = Encoding.ASCII.GetString(readbuffer, 0, readbuffer.Length - 1);
            

            if (recieve == false)
            {
                dwRes = WaitForSingleObject(osReader.EventHandle, UsbWaitTime);
                switch (dwRes)
                {
                    case WAIT_OBJECT_0:
                        if (!GetOverlappedResult((IntPtr)HidHandle, ref osReader, out nByte, false))
                        {
                            //nByte = 0;
                            break;
                        }
                        else
                        {
                            break;
                        }
                    case WAIT_OBJECT_0 + 1:
                        ok = GetOverlappedResult((IntPtr)HidHandle, ref osReader, out nByte, true);
                        break;
                    case WAIT_TIMEOUT:
                        break;
                    case WAIT_FAILED:
                        break;
                    default:
                        CancelIo((IntPtr)HidHandle);
                        nByte = 0;
                        break;

                }
            }

            CloseHandle(osReader.EventHandle);
            if (nByte == 0)
            {
                return 1;
            }
            else
            {
                return 0;
            }

        }

        #endregion

        #region override

        protected override void DoOpenPrinter(string printerName)
        {
            int index = 0;

            HidD_GetHidGuid(ref guidHID);

            while (true)
            {
                //muta++
                uint bufferSize = 0;
                PnPHandle = SetupDiGetClassDevs(ref GUID_DEVINTERFACE_USBPRINT, IntPtr.Zero, IntPtr.Zero, ClassDevsFlags.DIGCF_PRESENT | ClassDevsFlags.DIGCF_DEVICEINTERFACE);



                SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
                DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData);




                bool result = SetupDiEnumDeviceInterfaces(PnPHandle, IntPtr.Zero, ref GUID_DEVINTERFACE_USBPRINT, (uint)index, ref DeviceInterfaceData);
                bool result2 = false;
                int test = 0;
                UInt32 nBytes = 256;
                UInt32 nRequiredSize;

                //for (int devindex = 0; SetupDiEnumDeviceInterfaces(PnPHandle, IntPtr.Zero, ref GUID_DEVINTERFACE_USBPRINT, (uint)index, ref DeviceInterfaceData); ++devindex)
                //{

                result = SetupDiEnumDeviceInterfaces(PnPHandle, IntPtr.Zero, ref GUID_DEVINTERFACE_USBPRINT, (uint)index, ref DeviceInterfaceData);


                SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA();
                DeviceInfoData.cbSize = Marshal.SizeOf(DeviceInfoData);
                test = SetupDiEnumDeviceInfo(PnPHandle, index, ref DeviceInfoData);

                result = SetupDiGetDeviceInterfaceDetail(PnPHandle, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, DeviceInfoData);

                //PSP_DEVICE_INTERFACE_DETAIL_DATA psp_detail = new PSP_DEVICE_INTERFACE_DETAIL_DATA();


                IntPtr detailDataBuffer = Marshal.AllocHGlobal((int)bufferSize);
                Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);

                SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();

                detailData.cbSize = (uint)Marshal.SizeOf(detailData);
                //detailData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
                //psp_detail.cbSize = Marshal.SizeOf(detailData); 
                //Marshal.StructureToPtr(detailData, detailDataBuffer, false);



                result = SetupDiGetDeviceInterfaceDetail(PnPHandle, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, DeviceInfoData);


                if (result == true)
                {
                    string SPDRP_DEVICEDESC = "";
                    string SPDRP_CLASS = "";
                    string SPDRP_CLASSGUID = "";
                    string SPDRP_DRIVER = "";
                    string SPDRP_MFG = "";
                    string SPDRP_FRIENDLYNAME = "";

                    string SPDRP_LOWERFILTERS = "";

                    SPDRP_DEVICEDESC = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_DEVICEDESC);
                    SPDRP_CLASS = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_CLASS);
                    SPDRP_CLASSGUID = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_CLASSGUID);
                    SPDRP_DRIVER = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_DRIVER);
                    SPDRP_MFG = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_MFG);
                    SPDRP_FRIENDLYNAME = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_FRIENDLYNAME);//no
                    SPDRP_LOWERFILTERS = GetRegistryProperty(PnPHandle, ref DeviceInfoData, RegPropertyType.SPDRP_LOWERFILTERS);

                    //Console.WriteLine(SPDRP_DEVICEDESC);
                    //Console.WriteLine(SPDRP_CLASS);
                    //Console.WriteLine(SPDRP_CLASSGUID);
                    //Console.WriteLine(SPDRP_DRIVER);
                    //Console.WriteLine(SPDRP_MFG);
                    //Console.WriteLine(SPDRP_FRIENDLYNAME);
                    //Console.WriteLine(SPDRP_LOWERFILTERS);

                    IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4);
                    String devicePathName = Marshal.PtrToStringAuto(pDevicePathName);
                    Console.WriteLine(devicePathName);

                    for (int i = 0; i < 8; i++)
                    {
                        VID = VID + devicePathName[i + 8];
                    }

                    for (int i = 0; i < 8; i++)
                    {
                        PID = PID + devicePathName[i + 17];
                    }

                    uint bytewrite = 0;
                    HidHandle = CreateFile(devicePathName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                    if (HidHandle == -1)
                    {
                        //return 0;
                    }
                    else
                    {
                        //return 1;
                    }

                    //WriteToStream();
                    //CloseHandle((IntPtr)HidHandle);
                    return;

                }
            }
        }

        protected override void DoClosePrinter()
        {
            CloseHandle((IntPtr)HidHandle);
        }

        #endregion
    }
}
